Skip to content

[CrashReporter] Enable InProc CrashReporter for crashing GC threads - #131821

Open
mdh1418 wants to merge 3 commits into
dotnet:mainfrom
mdh1418:inproc-crash-report-gc-suspension
Open

[CrashReporter] Enable InProc CrashReporter for crashing GC threads#131821
mdh1418 wants to merge 3 commits into
dotnet:mainfrom
mdh1418:inproc-crash-report-gc-suspension

Conversation

@mdh1418

@mdh1418 mdh1418 commented Aug 4, 2026

Copy link
Copy Markdown
Member

Previously, the InProc CrashReporter skipped suspending and enumerating other managed threads during any GC scenario. However, in select GC scenarios, the thread running the reporter either owns the existing thread suspension or can safely establish its own suspension and walk managed call stacks.

This change tracks suspension ownership so the reporter can:

  • Reuse a completed suspension owned by the crashing thread.
  • Establish and later resume its own suspension when no GC suspension is active.
  • Continue avoiding thread enumeration when a suspension is incomplete or owned by another thread.

Reuse an existing suspension only when the crashing thread owns it, and allow workstation background GC to perform reporter-owned suspend and resume while preserving Server GC deadlock safeguards.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: aa976a9e-b875-4524-82e1-1485d03d14fa
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates CoreCLR’s in-proc crash-report stack walker to allow managed thread enumeration in additional GC/suspension scenarios by tracking whether a runtime suspension is (a) unavailable, (b) already established and owned by the crashing thread, or (c) created by the crash reporter.

Changes:

  • Introduces CrashReportSuspensionOwnership to distinguish between an unusable suspension, a usable existing suspension, and a reporter-created suspension.
  • Reuses an existing completed suspension only when the crashing thread owns the ThreadStore lock; otherwise avoids enumerating other managed threads.
  • Resumes the runtime only when the crash reporter itself performed the suspension.

Comment thread src/coreclr/vm/crashreportstackwalker.cpp Outdated
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

Comment thread src/coreclr/vm/crashreportstackwalker.cpp
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: aa976a9e-b875-4524-82e1-1485d03d14fa
Copilot AI review requested due to automatic review settings August 6, 2026 15:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Suppressed comments (3)

src/coreclr/vm/crashreportstackwalker.cpp:601

  • ThreadStore::GetThreadList() requires the current thread to hold the ThreadStore lock (it asserts s_pThreadStore->m_Crst.GetEnterCount() > 0). With the new CrashReportSuspensionOwnership::Existing path (e.g., stable Server GC worker where another thread owns the lock), this loop can trip that assert in checked builds and violates the existing ThreadStore locking contract.

To make the 'Existing but not owned by this thread' scenario safe, either (a) avoid enumerating other threads unless the current thread holds the ThreadStore lock, or (b) introduce an explicit "enumerate thread list while another thread holds the lock / SysIsSuspended" helper that does not require lock ownership, and use that here.

    if (suspensionOwnership != CrashReportSuspensionOwnership::Unavailable)
    {
        Thread* pThread = nullptr;
        while ((pThread = ThreadStore::GetThreadList(pThread)) != nullptr)
        {

src/coreclr/vm/crashreportstackwalker.cpp:534

  • The comment says we should avoid calling SuspendEE when a suspension is already in progress, but the code doesn't check SysIsSuspendInProgress(). As a result, the crash reporter can still call SuspendEE while another thread is mid-suspension, which risks blocking on the ThreadStore lock (exactly what the comment says to avoid).
    if (g_fFatalErrorOccurredOnGCThread
        || GCHeapUtilities::IsGCInProgress()
        || crashThreadOwnsSuspension)
    {
        return CrashReportSuspensionOwnership::Unavailable;
    }

src/coreclr/inc/gccrashreport.h:12

  • This introduces a direct VM->GC call surface (SVR::IsGCThreadUsingStableSuspension) that bypasses the existing standalone-GC loading/probing mechanism (GC_VersionInfo/GC_Initialize via direct link vs GetProcAddress). In builds that load a standalone GC, this symbol may not exist / may not be resolvable the same way, and even if it does, calling it directly would not follow the selected GC module.

Consider plumbing this through the same mechanism as other GC entrypoints (an extern "C" exported function resolved during GC load and stored as a function pointer, or an existing GC interface contract) and make the header comment match the actual behavior/availability guarantees.

// Crash reporting entry points implemented by the linked-in GC. Standalone GCs
// conservatively return false because they do not execute this linked-in GC code.
namespace SVR
{
    bool IsGCThreadUsingStableSuspension();
}

Comment thread src/coreclr/gc/collect.cpp Outdated
Comment thread src/coreclr/vm/crashreportstackwalker.cpp Outdated
Track blocking Server GC participation through the final join so the in-process crash reporter can safely reuse the existing suspension without racing RestartEE.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: aa976a9e-b875-4524-82e1-1485d03d14fa
@mdh1418
mdh1418 force-pushed the inproc-crash-report-gc-suspension branch from ff8562c to 964cdd9 Compare August 6, 2026 18:38
Copilot AI review requested due to automatic review settings August 6, 2026 18:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Note

This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.

@lateralusX lateralusX left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@lateralusX
lateralusX self-requested a review August 7, 2026 07:59
{
// Foreground Server GC workers are non-suspendable GC-special threads.
// Suspendable background GC threads have an associated EE Thread.
bool isServerGCWorker = IsGCSpecialThread() && pCrashThread == nullptr;

@lateralusX lateralusX Aug 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is pCrashThread always current thread? If so, maybe we should enforce it since we use API to check current thread type together with state of pCrashThread. Alternative is not to do this if pCrashThread is not current thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants